//go:build linux || windows

// Add By : 陈狍子 e4ting@qq.com 2024-05-19 12:32:14

package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"
	"os/exec"
	"reflect"
	"strings"

	"github.com/gammazero/nexus/v3/client"
	"github.com/gammazero/nexus/v3/wamp"
	"github.com/google/shlex"
)

type Reg struct {
	Procedure string                   `json:"func"`
	fn        client.InvocationHandler `json:"-"`
}

type Sub struct {
	Topic string              `json:"topic"`
	fn    client.EventHandler `json:"-"`
}

var (
	fn_fuse     reflect.Value
	callee      *client.Client
	Registers   []Reg
	Subscribers []Sub
)

func init() {
	fuse := Fuse{}
	fuse.Init()
	fn_fuse = reflect.ValueOf(&fuse)
}

func InitWAMP(logger *log.Logger) *client.Client {
	cfg := client.Config{
		Realm:         *realm,
		Serialization: client.JSON,
		Logger:        logger,
		TlsCfg:        &tls.Config{InsecureSkipVerify: true},
	}
	cfg.WsCfg.EnableCompression = true

	// Create client with requested transport type.
	var cli *client.Client
	var err error

	cli, err = client.ConnectNet(CtxMain, *ws_url, cfg)
	if err != nil {
		return nil
	}
	logger.Println("启动")
	return cli
}

func RpcInstall(NodeID string) {
	// Connect callee client with requested socket type and serialization.
	callee = InitWAMP(logger)
	Registers = []Reg{
		{fmt.Sprintf("e4ting.%s.fuse", NodeID), RPC_Proxy},
		{fmt.Sprintf("e4ting.%s.run", NodeID), RPC_run},
		{fmt.Sprintf("e4ting.%s.update", NodeID), RPC_SelfUpdate},
	}
	Subscribers = []Sub{
		{fmt.Sprintf("e4ting.keepalive"), RPC_Keepalive},
		{fmt.Sprintf("e4ting.update"), RPC_Update},
	}
	for _, item := range Registers {
		if err := callee.Register(item.Procedure, item.fn, nil); err != nil {
			logger.Fatal("Failed to register Procedure:", err)
		}
		logger.Println("注册函数", item.Procedure)
	}
	for _, item := range Subscribers {
		if err := callee.Subscribe(item.Topic, item.fn, nil); err != nil {
			logger.Fatal("Failed to Subscribe Topic:", err)
		}
		logger.Println("订阅主题", item.Topic)
	}
}

func RpcUnInstall() {
	for _, item := range Registers {
		if err := callee.Unregister(item.Procedure); err != nil {
			logger.Println("Failed to unregister Procedure:", err)
		}
	}
	callee.Close()
}

func RPC_Keepalive(event *wamp.Event) {
	logger.Println(`keepalive !`)
	node.ReportHardware()
}

func do_update(args wamp.List) {
	url := "http://consul.e4ting.cn/e4ting/cluster.tar.gz?raw"
	// logger.Println(len(args), args)
	if len(args) > 0 {
		temp := args[0].(string)
		if len(temp) > 0 {
			url = args[0].(string)
			logger.Println(`更新地址为`, url)
		}
	}
	go func(url string) {
		up := Upgrader{}
		up.Init(url)
		defer up.Cleanup()
		up.DoUpdate()
	}(url)
}

func RPC_Update(event *wamp.Event) {
	logger.Println(`收到更新命令`)
	ar, _ := wamp.AsList(event.Arguments)
	do_update(ar)
}

func RPC_SelfUpdate(ctx context.Context, inv *wamp.Invocation) client.InvokeResult {
	// 灰度更新
	logger.Println(`开始灰度更新`)
	ar, _ := wamp.AsList(inv.Arguments)
	do_update(ar)
	return client.InvokeResult{Args: wamp.List{}}
}

func RPC_Proxy(ctx context.Context, inv *wamp.Invocation) client.InvokeResult {
	args, _ := wamp.AsList(inv.Arguments)
	logger.Printf("%+v", args[:2])
	// info := CallFunction(ret[0].(string), ret[1:]...)
	info := func(name string, args ...interface{}) interface{} {
		in := make([]reflect.Value, len(args))
		for i, arg := range args {
			if v, ok := arg.(uint64); ok {
				// 如果是 uint64，进行转换为 int 的操作
				in[i] = reflect.ValueOf(int(v))
			} else {
				in[i] = reflect.ValueOf(arg)
			}
		}
		// logger.Printf("%+v", in)
		// 调用函数 strings.Title 首字母大写
		return fn_fuse.MethodByName(strings.Title(name)).Call(in)[0].Interface()
	}(args[0].(string), args[1:]...)
	return client.InvokeResult{Args: wamp.List{info}}
}

func RPC_run(ctx context.Context, inv *wamp.Invocation) client.InvokeResult {
	// var params []string
	ret, _ := wamp.AsList(inv.Arguments)
	params, _ := wamp.ListToStrings(ret)
	if len(params) == 1 {
		params, _ = shlex.Split(params[0])
	}
	logger.Println(params)
	info := func(c string, args ...string) string {
		output, err := exec.Command(c, args...).CombinedOutput()
		if err != nil {
			logger.Println(err)
			return err.Error()
		}
		return string(output)
	}(params[0], params[1:]...)
	return client.InvokeResult{Args: wamp.List{info}}
}
